home *** CD-ROM | disk | FTP | other *** search
/ Aminet 4 / Aminet 4 - November 1994.iso / aminet / comm / net / spakparnet_0_5.lha / par / par.s < prev    next >
Text File  |  1992-11-09  |  18KB  |  559 lines

  1.     include ":stone/dazsys/macros.i"
  2.     include ":stone/dazsys/xrefs.i"
  3.  
  4. ;
  5. ; Parrallel port driver (c) Spak, SST Scientific Endeavours
  6. ; currently implemented for the parallel port
  7. ; contact: c9107253@xenon.newcastle.edu.au (1994)
  8. ;
  9. ; Cable is the same as that for parnet
  10. ;
  11. ; I didn't use the parnet.device because I couldn't find it at the time
  12. ;
  13. ; this is a pretty pathetic method of pumping data thru the parallel port
  14. ; since it is so proc'r intensive & I think that the parallel port limits the
  15. ; speed at which data is pumped anyway
  16. ;
  17. ; assume that all lines are pulled high (if 2 outputs drive the same line
  18. ; then the resulting line state will be an active-low 'OR')
  19. ;
  20. ;
  21. ; Note that there is a bit of a patch in the acquiring of the port for sending
  22. ; (SetupSend) and the interrupt "on" stuff was moved into the send.c module
  23.  
  24.  
  25. **************************** PARNET CABLE COMPATIBLE *****************************
  26.  
  27.  
  28. ;   CABLE:  Connect D7-D0,SEL,POUT, and BUSY across,
  29. ;        Connect ACK to SEL locally:
  30. ;        Connect GND lines indicated
  31. ;
  32. ;   (2-9)   D7-D0   ------------    D7-D0
  33. ;   (12)    POUT    ------------    POUT
  34. ;   (11)    BUSY    ------------    BUSY       PARALLEL PORT
  35. ;   (13)    SEL     --+------+--    SEL
  36. ;   (10)    ACK     -/        \-    ACK
  37. ;   (18-22) GND     ------------    GND
  38. ;
  39. ;   WARNING: you cannot connect RI on the serial port to your
  40. ;         modem because it interferes with the parallel
  41. ;         port's SEL line in this configuration.
  42.  
  43.  
  44.  
  45.         xdef        _SendData                    ;d0/a0/a1
  46.         xdef        _ReceiveData                ;d0/a0/a1
  47.         xdef        _BackSendData                ;d0/a0/a1
  48.         xdef        _BackReceiveData            ;d0/a0/a1
  49.         xdef        _RelinquishPort                ;none
  50.         xdef        _SetupSend                    ;d0/a1 : d0
  51.         xdef        _SetupReceive                ;a1 : d0
  52.  
  53.         xdef        _CIAIntRoutine                ;to be called in the interrupt
  54.  
  55.         xdef        _ChkSum                        ;a0/d1 : d0
  56.         OPT        L+
  57.  
  58.  
  59. ; DATA LINE LOCATIONS (8)
  60. inputaddress    = $bfe101    ;parallel port
  61. outputaddress    = $bfe101    ;same port
  62. datadirection    = $bfe301    ;direction control (0=input, 1=output) as per CIA
  63.  
  64. ; HANDSHAKING CONTROL LINES
  65. syncaddress        = $bfd000    ;control bits as per CIA (set or read these)
  66. syncdirection    = $bfd200    ;direction of sync lines as per CIA (ie LOW bits for input, HIGH  bits for output)
  67. validbit        = 1            ;Data on bus is valid (POUT)
  68. ackbit            = 0            ;Read data from bus (BUSY)
  69. selbit            = 2            ;used to trigger interrupts
  70.  
  71. ; defines of errors
  72. PAR_OK            = 0
  73. PAR_ERROR_TIMEOUT = 1
  74. PAR_ERROR_BUSY = 2            ;actually means "timeout on busy"
  75.  
  76.  
  77.  
  78. ; CIA interrupt number (using the ciab.resource)
  79. CIAINTB = 4
  80. CIAINTF = (1<<CIAINTB)
  81. AbleICR = -$12
  82. SetICR = -$18
  83.  
  84.  
  85. **************************************************************************
  86. ICR_OFF MACRO    ;A6 must already have the CIA base
  87. **************************************************************************
  88. ; THE PROPPER WAY, WHERE a6 = cia.resource_base
  89.     move.l        #CIAINTF,d0
  90.     jsr            AbleICR(a6)                        ;don't interrupt us
  91. ;    move.b        #CIAINTF,$bfed01                ; THE NAUGHTY WAY
  92.     ENDM
  93.  
  94. **************************************************************************
  95. ICR_ON MACRO    ;A6 must already have the CIA base
  96. **************************************************************************
  97. ; THE PROPPER METHOD
  98.     move.l        #(1<<7)+CIAINTF,d0
  99.     jsr            AbleICR(a6)                        ;interrupts back on 
  100. ;    move.b        #(1<<7)+CIAINTF,$bfed01            ;naughty method
  101.     ENDM
  102.  
  103.  
  104. **************************************************************************
  105.     ; MACROS
  106. **************************************************************************
  107.  
  108. ; Wait on a bit from a byte to become 0 or 1, with time out
  109. ; if "abort" becomes 1 then the routine branches to abort_label
  110. ;          \1     \2       \3        \4          \5
  111. ; WAITBIT state,bitnumber,address ,abort_address, abort_label
  112. WAITBIT    MACRO
  113.   __wbklfj_loop_\@:
  114.     btst.b        \2,\3
  115.     IFC        '\1','0'
  116.     beq.s      __djdjjd_ok\@
  117.     ELSEIF
  118.     bne.s      __djdjjd_ok\@
  119.     ENDC
  120.     tst.b    \4
  121.     bne        \5
  122.     bra.s    __wbklfj_loop_\@
  123.   __djdjjd_ok\@:
  124.     ENDM
  125.  
  126. WAITVALIDHIGH    MACRO
  127.     WAITBIT    1,#validbit,\1,\2,\3
  128.     ENDM
  129. WAITVALIDLOW    MACRO
  130.     WAITBIT    0,#validbit,\1,\2,\3
  131.     ENDM
  132. WAITACKHIGH        MACRO
  133.     WAITBIT    1,#ackbit,\1,\2,\3
  134.     ENDM
  135. WAITACKLOW        MACRO
  136.     WAITBIT    0,#ackbit,\1,\2,\3
  137.     ENDM
  138. SETVALIDHIGH    MACRO
  139.     bset.b    #validbit,\1
  140.     ENDM
  141. SETVALIDLOW        MACRO
  142.     bclr.b    #validbit,\1
  143.     ENDM
  144. SETACKHIGH        MACRO
  145.     bset.b    #ackbit,\1
  146.     ENDM
  147. SETACKLOW        MACRO
  148.     bclr.b    #ackbit,\1
  149.     ENDM
  150. SETSELHIGH        MACRO
  151.     bset.b    #selbit,\1
  152.     ENDM
  153. SETSELLOW        MACRO
  154.     bclr.b    #selbit,\1
  155.     ENDM
  156. WAITSELHIGH    MACRO
  157.     WAITBIT    1,#selbit,\1,\2,\3
  158.     ENDM
  159. WAITSELLOW    MACRO
  160.     WAITBIT    0,#selbit,\1,\2,\3
  161.     ENDM
  162.  
  163.  
  164. ; HANDSHAKING DONE IN SOFTWARE HERE! (SLOW!!!)
  165.  
  166. **************************************************************************
  167. ; Input: a0 memory buffer to send/receive
  168. ;         a1 abort flag (when = 0, abort!)
  169. ;         a4 input/output port address
  170. ;         a2 control address (with directions already set)
  171. ;         d0 number of words (ie bytes/2) to get
  172. ;
  173. ;         d2 read data only - temp (should be cleared to begin with
  174. ;         d1 read data only - updated checksum (should be cleared to begin with)
  175. ;         d3 valid bit number (input-read, output-write)
  176. ;         d4 ack bit number (output-read, input-write)
  177.  
  178. ; Valid/Acknowledge bits should be high if sending/receiving for the first time
  179. ; Note: these start on waiting/setting acknowledge LOW and end on waiting/setting
  180. ; a HIGH, which means the ports can be swapped between input and output with
  181. ; no effect (since hi-z drifts high)
  182. **************************************************************************
  183.         ; READ DATA LOOP
  184. **************************************************************************
  185. rd_mainloop
  186.         WAITBIT        0,d3,(a2),(a1),_rd_done        ;WAIT VALID LOW
  187.         move.b        (a4),d2                        ;read it
  188.         bclr.b        d4,(a2)                        ;SET ACK LOW
  189.         add.w        d2,d1                        ;chk sum
  190.         move.b        d2,(a0)+                    ;put it in memory
  191.         WAITBIT        1,d3,(a2),(a1),_rd_done        ;WAIT VALID HIGH
  192.         move.b        (a4),d2                        ;read it
  193.         bset.b        d4,(a2)                        ;SET ACK HIGH
  194.         add.w        d2,d1                        ;chk sum
  195.         move.b        d2,(a0)+                    ;put it in memory
  196. _ReadData
  197.         dbf.w        d0,rd_mainloop
  198.         addq.w        #1,d0                        ;fix -1 to 0
  199.     _rd_done:
  200.         rts
  201.  
  202.  
  203. **************************************************************************
  204.         ; WRITE DATA LOOP
  205. **************************************************************************
  206. wd_mainloop:
  207.         move.b        (a0)+,(a4)                    ;write it
  208.         bclr.b        d3,(a2)                        ;SET VALID LOW
  209.         WAITBIT        0,d4,(a2),(a1),_wd_done        ;WAIT ACK LOW
  210.         move.b        (a0)+,(a4)                    ;write it
  211.         bset.b        d3,(a2)                        ;SET VALID HIGH
  212.         WAITBIT        1,d4,(a2),(a1),_wd_done        ;WAIT ACK LOW
  213. _WriteData
  214.         dbf.w        d0,wd_mainloop
  215.         addq.w        #1,d0                        ;fix -1 to 0
  216.     _wd_done:
  217.         rts
  218.  
  219.  
  220. ; have to do the handshake in pairs... so we read one byte and
  221. ; handshake for the second
  222. **************************************************************************
  223.         _Read1Byte:
  224. **************************************************************************
  225.         WAITBIT        0,d3,(a2),(a1),_r1b_done    ;WAIT VALID LOW
  226.         move.b        (a4),d2                        ;read it
  227.         bclr.b        d4,(a2)                        ;SET ACK LOW
  228.         add.w        d2,d1                        ;chk sum
  229.         move.b        d2,(a0)+                    ;put it in memory
  230.         WAITBIT        1,d3,(a2),(a1),_r1b_done    ;WAIT VALID HIGH
  231.         bset.b        d4,(a2)                        ;SET ACK HIGH
  232.         subq.w        #1,d0                        ;read 1 (which counts as 2)
  233.   _r1b_done:
  234.         rts
  235.  
  236. ; We could get away with just writing out an extra byte but with this
  237. ; we will never go outside our given buffer
  238. **************************************************************************
  239.         _Write1Byte:
  240. **************************************************************************
  241.         move.b        (a0)+,(a4)                    ;write it
  242.         bclr.b        d3,(a2)                        ;SET VALID LOW
  243.         WAITBIT        0,d4,(a2),(a1),_w1b_done    ;WAIT ACK LOW
  244.         bset.b        d3,(a2)                        ;SET VALID HIGH
  245.         WAITBIT        1,d4,(a2),(a1),_w1b_done    ;WAIT ACK LOW
  246.         subq.w        #1,d0
  247.     _w1b_done:
  248.         rts
  249.  
  250.  
  251.  
  252. **************************************************************************
  253.         _ReceiveData:
  254. **************************************************************************
  255. ;input: d0.w = bytes to get (max 32000)
  256. ;        a0 = dest address
  257. ;        a1 = abort flag address (aborts if flag goes to 0)
  258. ;        a2 = check sum address (word)
  259. ; a0 is updated
  260. ; d0 is returned with the number of WORDS NOT SENT
  261. ; SetupReceive should be called prior to this routine
  262. **************************************************************************
  263.         movem.l        d1-d7/a1-a6,-(sp)
  264.         move.l        a2,a6                            ;a6 <== check sum address
  265.         moveq        #0,d1                            ;check sum
  266.         lea            syncaddress,a2                    ;control reg
  267.         lea            inputaddress,a4                    ;DATA reg
  268.         moveq        #0,d2                            ;temporary (0)
  269.         move.b        d2,datadirection                ;ALL 8 INPUT
  270.         moveq        #validbit,d3
  271.         moveq        #ackbit,d4
  272.  
  273.         lsr.w        #1,d0                            ;BYTES -> WORDS
  274.         if            cs,do,<addq.w #1,d0>,<bsr _Read1Byte>
  275.         bsr            _ReadData
  276.         move.w        d1,(a6)                            ;checksum return
  277.         movem.l        (sp)+,d1-d7/a1-a6
  278.         rts
  279.  
  280. **************************************************************************
  281.         _BackSendData:
  282. **************************************************************************
  283. ; this is the same as the send data except the valid/ack lines are
  284. ; reversed in roles (use this for instantly swapping between receiving
  285. ; and sending in the same packet)
  286. ; also the data has to be EVEN in length
  287. ;
  288. ;Send Data (Call SetupSend first)
  289. ;input: d0.w = number of bytes (MUST BE EVEN, NO CHECKING)
  290. ;         a0 = location to send
  291. ;        a1 = abort flag address (aborts if flag goes to 0)
  292. ; a0 is updated, d0 is returned with the number of words NOT sent
  293. ; SETUP RECEIVE SHOULD BE CALLED PRIOR TO THIS
  294. **************************************************************************
  295.         movem.l        d1-d7/a1-a6,-(sp)
  296.         lea            syncaddress,a2                    ;parallel control
  297.         lea            outputaddress,a4                ;parallel port out
  298.         move.b        #$ff,datadirection                ;all 8 lines output
  299.         moveq        #validbit,d4                    ;SWAPPED!
  300.         moveq        #ackbit,d3
  301.         lsr.w        #1,d0                            ;BYTES -> WORDS
  302.         bsr            _WriteData
  303.         movem.l        (sp)+,d1-d7/a1-a6
  304.         rts
  305.  
  306.  
  307.  
  308. **************************************************************************
  309.         _SendData:
  310. **************************************************************************
  311. ;Send Data (Call SetupSend first)
  312. ;input: d0.w = number of bytes
  313. ;         a0 = location to send
  314. ;        a1 = abort flag address (aborts if flag goes to 0)
  315. ; a0 is updated, d0 is returned with the number of words NOT sent
  316. ; SetupSend should be called prior to calling this
  317. **************************************************************************
  318.         movem.l        d1-d7/a1-a6,-(sp)
  319.         lea            syncaddress,a2                    ;parallel control
  320.         lea            outputaddress,a4                ;parallel port out
  321.         move.b        #$ff,datadirection                ;all 8 lines output
  322.         moveq        #validbit,d3
  323.         moveq        #ackbit,d4
  324.  
  325.         lsr.w        #1,d0                            ;BYTES -> WORDS
  326.         if            cs,do,<addq.w #1,d0>,<bsr _Write1Byte>
  327.         bsr            _WriteData
  328.         movem.l    (sp)+,d1-d7/a1-a6
  329.         rts
  330.  
  331.  
  332. **************************************************************************
  333.         _BackReceiveData:
  334. **************************************************************************
  335. ;input: d0.w = bytes to get (max 32000) (MUST BE EVEN FOR THIS!)
  336. ;        a0 = dest address
  337. ;        a1 = abort flag address (aborts if flag goes to 0)
  338. ;        a2 = check sum address (word)
  339. ; a0 is updated
  340. ; d0 is returned with the number of WORDS NOT SENT
  341. ; SetupSend should be called prior to this routine
  342. **************************************************************************
  343.         movem.l        d1-d7/a1-a6,-(sp)
  344.         move.l        a2,a6                        ;a6 <== check sum address
  345.         moveq        #0,d1                        ;check sum
  346.         lea            syncaddress,a2                ;control reg
  347.         lea            inputaddress,a4                ;DATA reg
  348.         moveq        #0,d2                        ;temporary
  349.         move.b        d2,datadirection            ;We want to read 8 lines
  350.         moveq        #validbit,d4                ;SWAPPED
  351.         moveq        #ackbit,d3
  352.         lsr.w        #1,d0                        ;BYTES -> WORDS
  353.         bsr            _ReadData
  354.         move.w        d1,(a6)                        ;checksum
  355.         movem.l        (sp)+,d1-d7/a1-a6
  356.         rts
  357.  
  358.  
  359. **************************************************************************
  360.         _RelinquishPort:
  361. **************************************************************************
  362.  
  363. ; simply makes all lines "input", relinquishing any control over the bus
  364. **************************************************************************
  365.         movem.l        d1-d7/a1-a6,-(sp)
  366.         lea            syncaddress,a2                    ;parallel control
  367.         lea            syncdirection,a3                ;parallel control
  368.         lea            outputaddress,a4                ;parallel port out
  369.  
  370. ** SET ALL TO INPUT (HI-Z)
  371.         move.b            #0,datadirection            ;all 8 lines input
  372.         SETVALIDLOW        (a3)                        ;hi-z input
  373.         SETACKLOW        (a3)                        ;hi-z input
  374.         SETSELLOW        (a3)                        ;hi-z input
  375.  
  376. ** SET ALL LINES HIGH (ie INACTIVE) JUST IN CASE..
  377.         SETSELHIGH        (a2)
  378.         SETACKHIGH        (a2)
  379.         SETVALIDHIGH    (a2)
  380.         move.b            #$ff,(a4)
  381.  
  382. ;        ICR_ON                                        ;(a6) should be already on!
  383.  
  384.         movem.l            (sp)+,d1-d7/a1-a6
  385.         rts
  386.  
  387.  
  388.  
  389. **************************************************************************
  390.         _SetupSend:
  391. **************************************************************************
  392. ;input: d0 = machine address to contact
  393. ;        a1 = address of abort flag
  394. ;        a6 = cia.resource base (so we can play with the interrupts)
  395.  
  396. ; call this when you want to start communications with another machine
  397. ; return (d0) 0 if we got the line ok, 1 if acquisition failed,
  398. ; 2 if machine not responding
  399. **************************************************************************
  400.         movem.l        d1-d7/a1-a6,-(sp)
  401.         move.l        a1,a5                            ;abort (affected by ICR control)
  402.         move.b        d0,d7                            ;machine ID
  403.         lea            syncaddress,a2                    ;parallel sync in/out
  404.         lea            syncdirection,a3                ;parallel control
  405.         lea            outputaddress,a4                ;parallel port out
  406.  
  407. ** TRY AND ACQUIRE THE LINE BY CHECKING FOR SEL HIGH
  408.         SETVALIDLOW        (a3)                        ;everything is an input
  409.         SETACKLOW        (a3)
  410.         SETSELLOW        (a3)
  411.  
  412. ; note that when nobody drives the line SEL *SHOULD* be high!
  413.         moveq            #PAR_ERROR_BUSY,d0            ;
  414.         WAITSELHIGH        (a2),(a5),_sus_done            ;acquire! (or bomb out)
  415.  
  416. ** CIA can't interrupt us (else we trigger ourselves)
  417.         ICR_OFF                                        ;(a6)
  418.  
  419. ** SEND OUT A NEGATIVE TRANSITION ON SEL (WHICH IS CONNECTED TO AN ACK)
  420.         SETSELHIGH        (a2)                        ;set / (order is important!)
  421.         SETVALIDLOW        (a2)                        ;set valid \ (for handshake)
  422.  
  423.         SETSELHIGH        (a3)                        ;SELECT IS AN OUTPUT
  424.         SETVALIDHIGH    (a3)                        ;"VALID" is an output
  425.         move.b        #$ff,datadirection                ;all 8 lines output
  426.         move.b        d7,(a4)                            ;put out dest machine
  427.  
  428. ** CAUSE AN INTERRUPT ON THE DEST MACHINE
  429.         SETSELLOW    (a2)                            ;SELECT negative edge
  430.  
  431.  
  432. ** WAIT FOR A LITTLE HANDSHAKE FROM ANOTHER MACHINE
  433.         moveq            #PAR_ERROR_TIMEOUT,d0
  434.         WAITACKLOW        (a2),(a5),_sus_intson        ;wait ack \
  435.         SETVALIDHIGH    (a2)                        ;set valid /
  436.         WAITACKHIGH        (a2),(a5),_sus_intson        ;wait ack /
  437.  
  438. ** WE DON'T WANT INTERRUPT CODE STARTING...
  439. ** this refuses to work properly here... moving it to send.c before relinquishport
  440. ;        move.l        #CIAINTF,d0                        ;Clear that interrupt
  441. ;        jsr            SetICR(a6)                        ;put this out of the way
  442. ;        ICR_ON                                        ;(a6) but turnem on
  443.  
  444.         moveq        #PAR_OK,d0                        ;everything - OK!
  445.     _sus_intson:
  446.  
  447.     _sus_done:
  448.         movem.l    (sp)+,d1-d7/a1-a6
  449.         rts
  450.  
  451.  
  452. **************************************************************************
  453.         _SetupReceive:
  454. **************************************************************************
  455. ;input: a1 is the abort flag address as usual
  456. ;return d0 = 0 if got the handshake, d0 = 1 if we timed out
  457. **************************************************************************
  458.         movem.l        d1-d7/a1-a6,-(sp)
  459.         lea            syncaddress,a2                    ;parallel control
  460.         lea            syncdirection,a3                ;parallel control
  461.         lea            outputaddress,a4                ;parallel port out
  462.         moveq    #PAR_ERROR_TIMEOUT,d0
  463.         SETVALIDLOW        (a3)                        ;"VALID" is an INPUT
  464.         SETACKHIGH        (a3)                        ;"ACK" is an OUTPUT
  465.         WAITVALIDLOW    (a2),(a1),_sur_done            ;WAIT valid \
  466.         SETACKLOW        (a2)                        ;set ack \
  467.         WAITVALIDHIGH    (a2),(a1),_sur_done            ;WAIT valid /
  468.         SETACKHIGH        (a2)                        ;set ack /
  469.         moveq    #PAR_OK,d0
  470.     _sur_done:
  471.         movem.l    (sp)+,d1-d7/a1-a6
  472.         rts
  473.  
  474.  
  475.  
  476. **************************************************************************
  477.         _CIAIntRoutine:
  478. **************************************************************************
  479. ; Please see the c-code part for the interrupt setup
  480. ; data_segment (is_Data) a1
  481. ; according to the ROM-kernal manual, d0/d1/d0/a1/a5 need not be saved
  482. ; Assume that the input port is already set up to take input data
  483. ; No handshaking is done here since that needs a timer to be set up
  484. ; after which setupreceive should be called for the first handshake
  485. ;
  486. ; count on the ciaa.resource to handle ALL of the interrupt hardware
  487. ; including the reseting of the CIA interrupt bits
  488. ;
  489. ; is_Data should point to
  490.                 rsset 0
  491. cir_sigmask        rs.l    1            ;ORDER IS IMPORTANT!
  492. cir_sigtask        rs.l    1
  493. cir_machine        rs.b    1            ;if (inputaddress) is this then signal
  494. cir_abort        rs.b    1            ;abort flag (set to 1)
  495. cir_pad            rs.b    2
  496. cir_ciares        rs.l    1            ;cia resource
  497.  
  498. **************************************************************************
  499.         movem.l    a6,-(sp)
  500.         move.l    a1,a5                                ;use a5 as the base
  501.  
  502. ;        move.w    #$f00,$dff180
  503.         move.b    #-1,cir_abort(a5)                    ;<-- this stuffs up
  504.  
  505. ** CHECK (inputaddress) AGAINST OUR MACHINE
  506.         move.b    inputaddress,d0
  507.         if.b    d0,ne,cir_machine(a5),cir_thatsall    ;is it for our machine?
  508.         move.b    #-2,cir_abort(a5)                    ;ABORT, in case we are
  509.                                                     ;still doing something else
  510.  
  511. ** SIGNAL OUR TASK
  512.         movem.l cir_sigmask(a5),d0/a1                ;get mask & task!
  513.         exec    Signal                                ;a1/d0 : ??
  514.  
  515.     cir_thatsall:
  516.         moveq    #0,d0
  517.         movem.l    (sp)+,a6
  518.         rts
  519.  
  520.  
  521. **************************************************************************
  522.     _ChkSum:
  523. **************************************************************************
  524. ; "FAST" check summer with unrolled loop
  525. ;input    a0 buffer (updated)
  526. ;        d1.w length (trashed)
  527. ;output d0 checksum
  528. **************************************************************************
  529.     movem.l    d2/d3,-(sp)
  530.  
  531.     move.w    d1,d2                            ;length
  532.     lsr.w    #4,d1                            ;counter
  533.     and.w    #15,d2                            ;leftover
  534.  
  535.     moveq    #0,d0                            ;sum
  536.     moveq    #0,d3                            ;temp
  537.  
  538. *** DO the bulk (should really do an offset into this..)
  539.     bra.s    _csy_skipa
  540. _csy_loopa:
  541.     REPT    16                                ;unroll this
  542.     move.b    (a0)+,d3
  543.     add.w    d3,d0
  544.     ENDR
  545. _csy_skipa:
  546.     dbf.w    d1,_csy_loopa
  547.  
  548. *** Do the remaining few
  549.     bra.s    _csy_skipb
  550. _csy_loopb:
  551.     move.b    (a0)+,d3
  552.     add.w    d3,d0
  553. _csy_skipb:
  554.     dbf.w    d2,_csy_loopb
  555.  
  556.     movem.l    (sp)+,d2/d3
  557.     rts
  558.